package edu.washington.ling.syntric.struct.projection;

import java.util.HashMap;
import java.util.List;
import java.util.LinkedList;

import edu.washington.ling.syntric.struct.rule.BiPSPattern;
import edu.washington.ling.syntric.struct.tree.Node;
import edu.washington.ling.syntric.struct.tree.Token;


public class BiProjectionAssembly {
	private HashMap<String, BiProjectionRule> spineMap;
	private HashMap<String, List<BiProjectionRule>> rootMap;
	private HashMap<String, List<BiProjectionRule>> headChildMap;
	
	public BiProjectionAssembly() {
		spineMap = new HashMap<String, BiProjectionRule>();
		rootMap = new HashMap<String, List<BiProjectionRule>>();
		headChildMap = new HashMap<String, List<BiProjectionRule>>();
	}
	
	private void addProjectionRule(BiProjectionRule projRule) {
		String spine = projRule.getSpine();
		if (spineMap.containsKey(spine)) {
			spineMap.get(spine).addToLeftDependentForest(projRule.getLeftDependentForest());
			spineMap.get(spine).addToRightDependentForest(projRule.getRightDependentForest());
		}
		else {
			spineMap.put(spine, projRule);
			String root = projRule.getRootLabel();
			if (rootMap.containsKey(root)) {
				rootMap.get(root).add(projRule);
			}
			else {
				List<BiProjectionRule> ruleList = new LinkedList<BiProjectionRule>();
				ruleList.add(projRule);
				rootMap.put(root, ruleList);
			}
			String headChild = projRule.getHeadChildLabel();
			if (headChildMap.containsKey(headChild)) {
				headChildMap.get(headChild).add(projRule);
			}
			else  {
				List<BiProjectionRule> ruleList = new LinkedList<BiProjectionRule>();
				ruleList.add(projRule);
				headChildMap.put(headChild, ruleList);
			}
		}
	}
	
	public List<ProjectionNode> connectNewChainsToHeadChildOf(BiProjectionNode parentProj) {
		List<ProjectionNode> newChains = new LinkedList<ProjectionNode>();
		for (BiProjectionRule rule: rootMap.get(parentProj.getHeadChildLabel())) {
			BiProjectionNode parentProjCopy = parentProj.clone();
			BiProjectionNode childProj = rule.toNode();
			parentProjCopy.setChild(childProj);
			newChains.add(parentProjCopy);
		}
		return newChains;
	}
	public List<ProjectionNode> connectNewChainsToRootOf(ProjectionNode childProj) {
		List<ProjectionNode> newChains = new LinkedList<ProjectionNode>();
		for (BiProjectionRule rule: headChildMap.get(childProj.getRootLabel())) {
			BiProjectionNode parentProj = rule.toNode();
			parentProj.setChild(childProj);
			newChains.add(parentProj);
		}
		return newChains;
	}
	
	public ProjectionNode connectNewChainToSpineOf(BiProjectionNode innerChain) {
		BiProjectionNode outerChain = spineMap.get(innerChain.getSpine()).toNode();
		for (List<ProjectionNode> depForest: innerChain.getLeftDependentForests())
			outerChain.insertLeftDependentForest(depForest);
		for (List<ProjectionNode> depForest: innerChain.getRightDependentForests())
			outerChain.insertRightDependentForest(depForest);
		outerChain.setChild(innerChain.getChild());
		return outerChain;
	}
	
	public void addProjectionsFromPSPatts(List<BiPSPattern> psPatts, Token t, boolean headFinal) {
		for (BiPSPattern psPatt: psPatts) {
			List<String> psRootStrs = new LinkedList<String>();
			for (Node psRootElem: psPatt.getRootChain()) {
				psRootStrs.add(psRootElem.getLabel());
			}
			String psDepChildLabel = psPatt.getDepChild().getLabel();
			String psHeadChildLabel = psPatt.getHeadChild().getLabel();
			BiProjectionRule projRule = new BiProjectionRule(psRootStrs, psHeadChildLabel);
			if (t.hasProjForestWithLabel(psDepChildLabel)) {
				List<ProjectionNode> depForest = t.getProjForestWithLabel(psDepChildLabel);
				if (headFinal)
					projRule.addToLeftDependentForest(depForest);
				else {
					projRule.addToRightDependentForest(depForest);
				}
				addProjectionRule(projRule);
			}
		}
	}
	
	public boolean hasSpine(String spine) {
		return spineMap.containsKey(spine);
	}
	
	public boolean hasHeadChild(String headChild) {
		return headChildMap.containsKey(headChild);
	}
	
	public BiProjectionRule getRuleForSpine(String spine) {
		return spineMap.get(spine);
	}
	
	public List<BiProjectionRule> getRulesForHeadChild(String headChild) {
		return headChildMap.get(headChild);
	}
	
	public boolean isEmpty() {
		return (headChildMap.size() == 0 && spineMap.size() == 0);
	}
}
